//
//  ShadingImage.m
//  CustomizedActionSheet
//
//  Created by 21CN Vgo on 11-3-24.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "ShadingImage.h"
#import <QuartzCore/QuartzCore.h> 


@interface ShadingImage()

+(ShadingImage*)shadingImage;

-(void)doAverageAxialShading:(CGContextRef)context size:(CGSize)size direction:(ShadingDirection)direction; 
-(CGFunctionRef)createFunctionForRGB:(CGFunctionEvaluateCallback)evaluationFunction;
-(CGColorSpaceRef)getTheCalibratedRGBColorSpace;
-(void)SetShadingColor:(UIColor *)clrStart endColor:(UIColor *)clrEnd;
-(void)SetTwoPartShadingColor:(UIColor *)clrStart endColor:(UIColor *)clrEnd startClr2nd:(UIColor *)clrStart2nd endClr2nd:(UIColor *)clrEnd2nd;
@end


@implementation ShadingImage

@synthesize redStart = _redStart;
@synthesize greenStart = _greenStart;
@synthesize blueStart = _blueStart;
@synthesize alphaStart = _alphaStart;

@synthesize redEnd = _redEnd;
@synthesize greenEnd = _greenEnd;
@synthesize blueEnd = _blueEnd;
@synthesize alphaEnd = _alphaEnd;

@synthesize redStart2nd = _redStart2nd;
@synthesize greenStart2nd = _greenStart2nd;
@synthesize blueStart2nd = _blueStart2nd;
@synthesize alphaStart2nd = _alphaStart2nd;

@synthesize redEnd2nd = _redEnd2nd;
@synthesize greenEnd2nd = _greenEnd2nd;
@synthesize blueEnd2nd = _blueEnd2nd;
@synthesize alphaEnd2nd = _alphaEnd2nd;

static ShadingImage* _shadingImage; // self

static void AverageRampEvaluate(void *info, const float *in, 
								float *out) 
{ 
	// The red component evaluates to 1 for an input value of 0, 
	// smoothly reduces to 0 
	
	ShadingImage * shadingObj = [ShadingImage shadingImage];
	// Red
	out[0] = shadingObj.redStart + (shadingObj.redEnd - shadingObj.redStart) * in[0]; 
	// Green
	out[1] = shadingObj.greenStart + (shadingObj.greenEnd - shadingObj.greenStart) * in[0]; 
	// Blue
	out[2] = shadingObj.blueStart + (shadingObj.blueEnd - shadingObj.blueStart) * in[0];
	// alpha 
	out[3] = shadingObj.alphaStart + (shadingObj.alphaEnd - shadingObj.alphaStart) * in[0]; 
	//NSLog(@"alpha:%.02f", out[3]);
} 


static void TwoPartAverageRampEvaluate(void *info, const float *in, 
							 float *out) 
{ 
	ShadingImage * shadingObj = [ShadingImage shadingImage];
	if (in[0] < 0.5) {
		// red component
		out[0] = shadingObj.redStart + 2 * in[0] * (shadingObj.redEnd - shadingObj.redStart); 
		// green component
		out[1] = shadingObj.greenStart + 2 * in[0] * (shadingObj.greenEnd - shadingObj.greenStart);  
		// blue component
		out[2] = shadingObj.blueStart + 2 * in[0] * (shadingObj.blueEnd - shadingObj.blueStart); 
		// alpha
		out[3] = shadingObj.alphaStart + 2 * in[0] * (shadingObj.alphaEnd - shadingObj.alphaStart);  
	}
	else {
		// red component
		out[0] = shadingObj.redStart2nd + 2 * (in[0] - 0.5) * (shadingObj.redEnd2nd - shadingObj.redStart2nd); 
		// green component
		out[1] = shadingObj.greenStart2nd + 2 * (in[0] - 0.5) * (shadingObj.greenEnd2nd - shadingObj.greenStart2nd); 
		// blue component
		out[2] = shadingObj.blueStart2nd + 2 * (in[0] - 0.5) * (shadingObj.blueEnd2nd - shadingObj.blueStart2nd); 
		// alpha
		out[3] = shadingObj.alphaStart2nd + 2 * (in[0] - 0.5) * (shadingObj.alphaEnd2nd - shadingObj.alphaStart2nd);   
	}
} 

#pragma mark singleton
- (id)init
{
	_redStart = 0;
	_greenStart = 0;
	_blueStart = 0;
	_alphaStart = 0;
	
	_redEnd = 0;
	_greenEnd = 0;
	_blueEnd = 0;
	_alphaEnd = 0;
	
	_redStart2nd = 0;
	_greenStart2nd = 0;
	_blueStart2nd = 0;
	_alphaStart2nd = 0;
	
	_redEnd2nd = 0;
	_greenEnd2nd = 0;
	_blueEnd2nd = 0;
	_alphaEnd2nd = 0;
	
	
	return self;
}

- (void)dealloc
{
	//SAFE_RELEASE(_clientID);
	[super dealloc];
}

+(ShadingImage*)shadingImage
{
	@synchronized(self)
	{
        if (_shadingImage == nil)
		{
            [[self alloc] init]; // assignment not done here
        }
    }
    return _shadingImage;
}

+ (id)allocWithZone:(NSZone *)zone
{
    @synchronized(self)
	{
        if (_shadingImage == nil)
		{
            _shadingImage = [super allocWithZone:zone];			
            return _shadingImage;  // assignment and return on first allocation
        }
    }
	
    return nil; //on subsequent allocation attempts return nil	
}

- (id)copyWithZone:(NSZone *)zone
{
	return self;
}

- (id)retain
{
	return self;
}

- (unsigned)retainCount
{
	return UINT_MAX;  //denotes an object that cannot be released
}

- (oneway void)release
{
	//do nothing
}

- (id)autorelease
{
	return self;
}

-(CGColorSpaceRef)getTheCalibratedRGBColorSpace
{ 
	static CGColorSpaceRef deviceRGB = NULL; 
	// Set once, the first time this function is called. 
	if(deviceRGB == NULL) 
		deviceRGB = CGColorSpaceCreateDeviceRGB(); 
	
	return deviceRGB; 
} 


//enum CGColorSpaceModel {
//	kCGColorSpaceModelUnknown = -1,
//	kCGColorSpaceModelMonochrome,		// 单色
//	kCGColorSpaceModelRGB,				// RGB
//	kCGColorSpaceModelCMYK,				// CMYK
//	kCGColorSpaceModelLab,				//
//	kCGColorSpaceModelDeviceN,			//
//	kCGColorSpaceModelIndexed,			//
//	kCGColorSpaceModelPattern			//
//};

-(void)SetShadingColor:(UIColor *)clrStart endColor:(UIColor *)clrEnd
{
	CGColorRef cgStart = clrStart.CGColor;
	int stModel = CGColorSpaceGetModel(CGColorGetColorSpace(cgStart));
	if (stModel == kCGColorSpaceModelRGB) {
		const CGFloat *clrComps = CGColorGetComponents(cgStart);
		self.redStart = clrComps[0];
		self.greenStart = clrComps[1];
		self.blueStart = clrComps[2];
		self.alphaStart = clrComps[3];
	} else if (stModel == kCGColorSpaceModelMonochrome) {
		const CGFloat *clrComps = CGColorGetComponents(cgStart);
		self.redStart = clrComps[0];
		self.greenStart = clrComps[0];
		self.blueStart = clrComps[0];
		self.alphaStart = clrComps[1];
	}
	
	
	CGColorRef cgEnd = clrEnd.CGColor;
	int endModel = CGColorSpaceGetModel(CGColorGetColorSpace(cgEnd));
	if (endModel == kCGColorSpaceModelRGB) {
		const CGFloat *clrComps = CGColorGetComponents(cgEnd);
		self.redEnd = clrComps[0];
		self.greenEnd = clrComps[1];
		self.blueEnd = clrComps[2];
		self.alphaEnd = clrComps[3];
	} else if (endModel == kCGColorSpaceModelMonochrome) {
		const CGFloat *clrComps = CGColorGetComponents(cgEnd);
		self.redEnd = clrComps[0];
		self.greenEnd = clrComps[0];
		self.blueEnd = clrComps[0];
		self.alphaEnd = clrComps[1];
	}
	//self.alphaEnd = clrComps2nd[CGColorGetNumberOfComponents(cgEnd) - 1];
}

-(void)SetTwoPartShadingColor:(UIColor *)clrStart endColor:(UIColor *)clrEnd startClr2nd:(UIColor *)clrStart2nd endClr2nd:(UIColor *)clrEnd2nd
{
	[self SetShadingColor:clrStart endColor:clrEnd];
	
	CGColorRef cgStart = clrStart2nd.CGColor;
	int stModel = CGColorSpaceGetModel(CGColorGetColorSpace(cgStart));
	const CGFloat *clrComps = CGColorGetComponents(cgStart);
	if (stModel == kCGColorSpaceModelRGB) {
		self.redStart2nd = clrComps[0];
		self.greenStart2nd = clrComps[1];
		self.blueStart2nd = clrComps[2];
		self.alphaStart2nd = clrComps[3];
	} else if (stModel == kCGColorSpaceModelMonochrome) {
		self.redStart2nd = clrComps[0];
		self.greenStart2nd = clrComps[0];
		self.blueStart2nd = clrComps[0];
		self.alphaStart2nd = clrComps[1];
	}
	//self.alphaStart2nd = clrComps[CGColorGetNumberOfComponents(cgStart) - 1];
	
	
	CGColorRef cgEnd = clrEnd2nd.CGColor;
	int endModel = CGColorSpaceGetModel(CGColorGetColorSpace(cgEnd));
	if (endModel == kCGColorSpaceModelRGB) {
		const CGFloat *clrComps = CGColorGetComponents(cgEnd);
		self.redEnd2nd = clrComps[0];
		self.greenEnd2nd = clrComps[1];
		self.blueEnd2nd = clrComps[2];
		self.alphaEnd2nd = clrComps[3];
	} else if (endModel == kCGColorSpaceModelMonochrome) {
		const CGFloat *clrComps = CGColorGetComponents(cgEnd);
		self.redEnd2nd= clrComps[0];
		self.greenEnd2nd = clrComps[0];
		self.blueEnd2nd = clrComps[0];
		self.alphaEnd2nd = clrComps[1];
	}

	//self.alphaEnd2nd = clrComps[CGColorGetNumberOfComponents(cgEnd) - 1];
}

-(CGFunctionRef)createFunctionForRGB:(CGFunctionEvaluateCallback)evaluationFunction
{ 
	CGFunctionRef function; 
	float domain[2];           // 1 input 
	float range[8];            // 4 outputs 
	CGFunctionCallbacks shadingCallbacks; 
	// Shadings parameterize the input between 0 (the starting point 
	// of the shading) and 1 (the ending point of the shading). 
	domain[0] = 0; domain[1] = 1; 
	
	// The range is the range for the output colors. For an RGB color 
	// space the values range from 0-1 for the R,G,B,A components. 
	// The red component, min and max. 
	range[0] = 0; range[1] = 1; 
	// The green component, min and max. 
	range[2] = 0; range[3] = 1; 
	// The blue component, min and max. 
	range[4] = 0; range[5] = 1; 
	// The alpha component, min and max. 
	range[6] = 0; range[7] = 1; 
	
	// The callbacks structure version is 
	// 0, the only defined version as of Tiger. 
	
	shadingCallbacks.version = 0; 
	// The routine Quartz should call to evaluate the function. 
	shadingCallbacks.evaluate = evaluationFunction; 
	// releaseInfo is NULL since there are no resources that need to 
	// be released when the function is released. 
	shadingCallbacks.releaseInfo = NULL; 
	
	// Dimension of domain is 1 and dimension of range is 4. 
	function = CGFunctionCreate(NULL, 1, domain, 4, range, 
								&shadingCallbacks); 
	if(function == NULL){ 
		fprintf(stderr, "Couldn't create the CGFunction!\n"); 
		return NULL; 
	} 
	return function; 
} 

-(void)doAverageAxialShading:(CGContextRef)context size:(CGSize)size direction:(ShadingDirection)direction 
{ 
	CGFunctionRef axialFunction; 
	CGShadingRef shading; 
	CGPoint startPoint, endPoint; 
	bool extendStart, extendEnd; 
	// This shading paints colors in the calibrated Generic RGB 
	// color space so it needs a function that evaluates 1 in to 4 out. 
	if (direction == ShadingDirection_VerticalTwoPart) {
		axialFunction = [self createFunctionForRGB:TwoPartAverageRampEvaluate];
	}else{
		axialFunction = [self createFunctionForRGB:AverageRampEvaluate];
	}
	if(axialFunction == NULL){ 
		return; 
	} 
	
	// Start the shading at 'startPoint' and end it at 'endPoint'. 
	// The axis of the shading is a line from 'startPoint' to 'endPoint' 
	
	if (direction == ShadingDirection_Horizon) {
		startPoint.x = 0; 
		startPoint.y = 0; 
		endPoint.x = size.width; 
		endPoint.y = 0;
	}else {
		startPoint.x = 0; 
		startPoint.y = 0; 
		endPoint.x = 0; 
		endPoint.y = size.height;
	}
	
	// Don't extend this shading. 
	extendStart = extendEnd = false; 
	
	shading = CGShadingCreateAxial([self getTheCalibratedRGBColorSpace], 
								   startPoint, endPoint, 
								   axialFunction, 
								   extendStart, extendEnd); 
	// The shading retains the function and this code 
	// is finished with the function so it should release it. 
	CGFunctionRelease(axialFunction); 
	if(shading == NULL){ 
		fprintf(stderr, "Couldn't create the shading!\n"); 
		return; 
	} 
	// Draw the shading. This paints the shading to the destination 
	// context, clipped by the current clipping area. 
	CGContextDrawShading(context, shading); 
	// Release the shading once the code is finished with it. 
	CGShadingRelease(shading); 
} 

+(UIImage *)AverageShadingImageWithStartColor:(UIColor *)clrStart endColor:(UIColor *)endColor size:(CGSize)size
{
	UIGraphicsBeginImageContext(size);
	
	CGContextRef ctx = UIGraphicsGetCurrentContext();
	[[self shadingImage] SetShadingColor:clrStart endColor:endColor];
	[[self shadingImage] doAverageAxialShading:ctx size:size direction:ShadingDirection_Vertical];
	CGImageRef cgImage = CGBitmapContextCreateImage(ctx);
	UIImage * image = [UIImage imageWithCGImage:cgImage];
	
	UIGraphicsEndImageContext();
	CGImageRelease(cgImage);
	
	return image;
}

+(UIImage *)TwoPartAverageShadingImageWithStartColor:(UIColor *)clrStart endColor:(UIColor *)clrEnd startColor2nd:(UIColor *)clrStart2nd endColor2nd:(UIColor *)clrEnd2nd size:(CGSize)size
{
	UIGraphicsBeginImageContext(size);
	
	CGContextRef ctx = UIGraphicsGetCurrentContext();
	[[self shadingImage] SetTwoPartShadingColor:clrStart endColor:clrEnd startClr2nd:clrStart2nd endClr2nd:clrEnd2nd];
	[[self shadingImage] doAverageAxialShading:ctx size:size direction:ShadingDirection_VerticalTwoPart];
	CGImageRef cgImage = CGBitmapContextCreateImage(ctx);
	UIImage * image = [UIImage imageWithCGImage:cgImage];
	
	UIGraphicsEndImageContext();
	CGImageRelease(cgImage);
	
	return image;
}

@end

